/*
 * Unidata Platform Community Edition
 * Copyright (c) 2013-2020, UNIDATA LLC, All rights reserved.
 * This file is part of the Unidata Platform Community Edition software.
 *
 * Unidata Platform Community Edition is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Unidata Platform Community Edition is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */
package org.unidata.mdm.dq.core.dto;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.annotation.Nullable;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.unidata.mdm.core.type.data.Attribute;
import org.unidata.mdm.dq.core.type.cleanse.CleanseFunctionOutputParam;
import org.unidata.mdm.dq.core.type.io.DataQualityError;
import org.unidata.mdm.dq.core.type.io.DataQualitySpot;

/**
 * @author Mikhail Mikhailov on Jan 21, 2021
 * CF execution result.
 */
public class CleanseFunctionResult {
    /**
     * Output by port name.
     */
    private Map<String, CleanseFunctionOutputParam> output;
    /**
     * The errors.
     */
    private List<DataQualityError> errors;
    /**
     * Failed local validation paths and values.
     */
    private List<DataQualitySpot> spots;
    /**
     * Constructor.
     */
    public CleanseFunctionResult() {
        super();
    }
    /**
     * Gets output param by port name.
     * @param portName the port name
     * @return param or null, if not found
     */
    public CleanseFunctionOutputParam getOutputParam(String portName) {
        return MapUtils.isNotEmpty(output) ? output.get(portName) : null;
    }
    /**
     * Gets output params as a collection.
     * @return params collection
     */
    public Collection<CleanseFunctionOutputParam> getOutputParams() {
        return MapUtils.isNotEmpty(output) ? output.values() : Collections.emptyList();
    }
    /**
     * Puts an output param to output map.
     * @param param the param value
     */
    public void putOutputParam(CleanseFunctionOutputParam param) {

        if (Objects.nonNull(param)) {

            if (Objects.isNull(output)) {
                output = new HashMap<>(8);
            }

            output.put(param.getPortName(), param);
        }
    }
    /**
     * Returns true, if this result has some output params set.
     * @return true, if this result has some output params set.
     */
    public boolean hasOutputParams() {
        return MapUtils.isNotEmpty(output) ;
    }
    /**
     * Gets output port names.
     * @return the names
     */
    public Collection<String> getOutputPorts() {
        return Objects.nonNull(output) ? output.keySet() : Collections.emptySet();
    }
    /**
     * Gets all collected errors.
     * @return the errors
     */
    public List<DataQualityError> getErrors() {
        return Objects.isNull(errors) ? Collections.emptyList() : errors;
    }
    /**
     * Adds an error to the error collection.
     * @param error the error to add
     */
    public void addError(DataQualityError error) {

        if (Objects.nonNull(error)) {

            if (Objects.isNull(errors)) {
                errors = new ArrayList<>(4);
            }

            errors.add(error);
        }
    }
    /**
     * Adds errors to the error collection.
     * @param errors the errors to add
     */
    public void addErrors(Collection<DataQualityError> errors) {
        if (CollectionUtils.isNotEmpty(errors)) {
            for (DataQualityError error : errors) {
                addError(error);
            }
        }
    }
    /**
     * Returns true, if the result has collected some errors.
     * @return true, if the result has collected some errors.
     */
    public boolean hasErrors() {
        return CollectionUtils.isNotEmpty(errors);
    }
    /**
     * Gets failed paths collection at whole.
     * @return failed paths collection
     */
    public List<DataQualitySpot> getSpots() {
        return Objects.isNull(spots) ? Collections.emptyList() : spots;
    }
    /**
     * Puts failed local paths to this result.
     * Note, incomplete paths are mapped to null, so one would use paths key set to find all paths.
     * @param path the path to append
     * @param attr the attribute tu put
     */
    public void addSpot(String path, @Nullable Attribute attr) {
        if (Objects.nonNull(path)) {
            addSpot(new DataQualitySpot(path, attr));
        }
    }
    /**
     * Adds a failure/problem spot as object.
     * @param failure the failure to add
     */
    public void addSpot(DataQualitySpot failure) {

        if (Objects.nonNull(failure)) {

            if (Objects.isNull(spots)) {
                spots = new ArrayList<>(4);
            }

            spots.add(failure);
        }
    }
    /**
     * Adds failure/problem spots to the spots collection.
     * @param failures the spots to add
     */
    public void addSpots(Collection<DataQualitySpot> failures) {
        if (CollectionUtils.isNotEmpty(failures)) {
            for (DataQualitySpot failure : failures) {
                addSpot(failure);
            }
        }
    }
    /**
     * Returns true, if this result contains failed validation paths.
     * @return true, if this result contains failed validation paths.
     */
    public boolean hasSpots() {
        return CollectionUtils.isNotEmpty(spots);
    }
}
